package xin.nick.security;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.util.CollectionUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import xin.nick.config.SystemProperties;
import xin.nick.constant.SystemConstants;
import xin.nick.entity.Result;
import xin.nick.entity.ResultCode;
import xin.nick.util.ResultUtil;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author Nick
 * @since 2022/7/20/020
 */
@Configuration
@EnableWebSecurity
@Slf4j
@EnableMethodSecurity()
@RequiredArgsConstructor
public class WebSecurityConfig {

    private final SystemProperties systemProperties;
    private final AuthenticationConfiguration authenticationConfiguration;
    private final TokenAuthenticationTokenFilter tokenAuthenticationTokenFilter;


    /**
     * 配置过滤器
     * @param http HttpSecurity
     * @return SecurityFilterChain
     * @throws Exception Exception
     */
    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        // 禁用 csrf
        http.csrf().disable();

        // 设置跨域
        http.cors().configurationSource(corsConfigurationSource());

        // 去掉session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 配置认证失败处理 异常返回
        http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            ResultUtil.responseResult(response, Result.custom(ResultCode.UNAUTHORIZED));
        });

        // 配置没有权限处理 异常返回
        http.exceptionHandling().accessDeniedHandler((request, response, authException) -> {
            response.setStatus(HttpStatus.FORBIDDEN.value());
            ResultUtil.responseResult(response, Result.custom(ResultCode.FORBIDDEN));
        });


        // 开放其他页面权限
        List<String> whiteList = systemProperties.getWhites();
        if (!CollectionUtils.isEmpty(whiteList)) {
            for (String ignoreUrl : whiteList) {
                http.authorizeHttpRequests().mvcMatchers(ignoreUrl).permitAll();
            }
        }
        // 验证,登录地址放行
        http.authorizeHttpRequests().mvcMatchers(SystemConstants.CODE_PATH).permitAll();
        http.authorizeHttpRequests().mvcMatchers(SystemConstants.LOGIN_PATH).permitAll();
        http.authorizeHttpRequests().mvcMatchers(SystemConstants.ERROR_PATH).permitAll();
        // 指定所有的OPTIONS请求直接通过
        http.authorizeHttpRequests().mvcMatchers(HttpMethod.OPTIONS).permitAll();

        // 指定拦截需要授权才可以登录的路径
        List<String> authList = systemProperties.getAuths();
        if (!CollectionUtils.isEmpty(authList)) {
            for (String authPath : authList) {
                http.authorizeHttpRequests().mvcMatchers(authPath).authenticated();
            }
        }


        // token过滤器
        http.addFilterBefore(tokenAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(tokenAuthenticationTokenFilter, LogoutFilter.class);

        return http.build();
    }

    /**
     * 配置跨域
     */
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Collections.singletonList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH",
                "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type",
                "x-auth-token"));
        configuration.setExposedHeaders(Collections.singletonList("x-auth-token"));

        configuration.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new
                UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);


        return source;
    }

    /**
     * 编写AuthenticationManager的bean
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }


    /**
     * 设置密码加密类
     * @return BCryptPasswordEncoder
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }



}
